package cn.flying.cloud.gateway.route;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.List;

import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson2.JSON;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import cn.flying.cloud.gateway.redis.service.RedisService;

/**
 * 基于redis作为路由的存储，实现路由加载、删除、新增
 * 注意：RouteDefinitionWriter的save和delete动态添加或删除路由后，这些更改是即时生效的，但不是持久化的。
 * 如果你想让路由在应用重启后仍然存在，需要有一个机制来持久化这些路由定义（比如数据库存储，并在应用启动时重新加载）。
 *
 * @author: admin
 * @date: 2024年05月08日 17:06
 * @version: 1.0
 */
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {

    private static final String GATEWAY_ROUTES = "gateway:routes";

    @Resource
    private RedisService redisService;

    /**
     * 加载所有的路由定义配置
     *
     * @return
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        redisService.redisTpl().opsForHash().values(GATEWAY_ROUTES).stream().forEach(routeDefinition -> {
            routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class));
        });
        return Flux.fromIterable(routeDefinitions);
    }

    /**
     * 新增路由定义配置
     * 这些更改是即时生效的，但不是持久化的。
     *
     * @param route
     * @return
     */
    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(routeDefinition -> {
            if (StringUtils.isEmpty(routeDefinition.getId())) {
                return Mono.error(new IllegalArgumentException("id may not be empty"));
            }
            redisService.redisTpl().opsForHash().put(GATEWAY_ROUTES, routeDefinition.getId(), JSON.toJSONString(routeDefinition));
            return Mono.empty();
        });
    }

    /**
     * 新增路由定义配置
     * 这些更改是即时生效的，但不是持久化的。
     *
     * @param routeId
     * @return
     */
    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            if (redisService.redisTpl().opsForHash().hasKey(GATEWAY_ROUTES, routeId)) {
                redisService.redisTpl().opsForHash().delete(GATEWAY_ROUTES, routeId);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
        });
    }

    /**
     * 刷新路由配置
     */
    public void refresh() {
        redisService.del(GATEWAY_ROUTES);
    }
}
